package net.code.btalk;

import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

import net.code.btalk.ui.BuddyListField;
import net.code.btalk.ui.BuddyScreen;
import net.code.btalk.ui.IconLabelField;
import net.code.btalk.ui.LoginScreen;
import net.code.btalk.ui.MessageTextField;
import net.code.btalk.ui.NotifyDialog;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.Dialog;
import net.sourceforge.jxa.Jxa;
import net.sourceforge.jxa.XmppListener;

public class BTalk extends UiApplication implements XmppListener {
	
	public static final String Version = "0.5.xxx";
	
	private Jxa jxa;
	private LoginScreen loginscreen;
	private String _name;
	private String _jid;
	private String _password;
	private String _server;
	public int retryCount;
	
	public BuddyListField buddyList;
	public BuddyScreen buddyscreen;
	
//	private MessageScreen msgScreen;
//	private MessageTextField msgText;
	
	public Buddy currentBuddy;
	
	private int unreadCount;
	private byte[] unreadLock = new byte[0];
	
	// status values
	public int state;
	public final static int STATE_STARTUP = 0x0;
	public final static int STATE_LOGINING = 0x1;
	public final static int STATE_ONLINE = 0x2;
	public final static int STATE_FAILED = 0x3;
	public final static int STATE_WAITING = 0x4;
	public final static int STATE_RETRYING = 0x5;
	//boolean login;

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		BTalk app = new BTalk();
		
		// initialize static variables
		BuddyScreen.btalk = app;
		Buddy.btalk = app;
		MessageTextField.btalk = app;
		LoginScreen.btalk = app;
		Notification.btalk = app;
		
		Notification.midStream = app.getClass().getResourceAsStream("/mid/new_msg.mid");
		Notification.midStream.mark(150);
		app.enterEventDispatcher();
	}
	
	public BTalk() {
		this.state = BTalk.STATE_STARTUP;
		Vector up = SavedData.getUserInfo();
		if (up != null) {
			loginscreen = new LoginScreen(true, (String)up.elementAt(0), (String)up.elementAt(1));
		} else {
			loginscreen = new LoginScreen(false, null, null);
		}
		
		SavedData.readOptions();
		
		
		
		this.pushScreen(loginscreen);
		unreadCount = 0;
		this.retryCount = 0;
	}
	
	/*
	 * Only called by MessageTextField and currentBuddy mustn't be null
	 */
	public void sendMessage(String msg) {
		jxa.sendMessage(currentBuddy.jid, msg);
//		currentBuddy.getMsgList().sendMsg(msg);
		currentBuddy.sendMessage(msg);
	}
	
	public void openBuddy(Buddy b) {
		if (b.unread) {
			synchronized (unreadLock) {
				--unreadCount;
				if (unreadCount == 0) {
					Notification.clearNotification();
				}
			}
			b.unread = false;
		}
		buddyList.buddyReposition(b);
		this.pushScreen(b.getMsgScreen());
//		msgScreen.deleteAll();
//		msgScreen.setTitle("Chat with " + b.name);
//		msgScreen.initFields(b.getMsgList(), msgText);
//		msgText.setFocus();
//		this.pushScreen(msgScreen);
	}
	
	// Only called by LoginScreen
	public void loginJxa(String username, String password) {
		if (this.state != BTalk.STATE_LOGINING) {
			this.state = BTalk.STATE_LOGINING;
			loginscreen.logginState.setText("Status: Loginning in...");
			this._name = new String(username);
			if (username.indexOf("@") == -1)
				this._jid = username + "@gmail.com";
			else
				this._jid = new String(username);
			this._password = new String(password);
			this._server = "talk.google.com";
			
			jxa = new Jxa(this._jid, password, "mobile", 10, "talk.google.com", "5223", true);
			jxa.addListener(this);
			jxa.start();
		}
	}
	
	public void setMyStatus(int s, boolean customText, String text) {
		switch (s) {
		case BTalk.STATE_ONLINE:
			buddyscreen.statusBanner = new IconLabelField(BuddyListField.onlineIcon, "Available");
			buddyscreen.setTitle(buddyscreen.statusBanner);
//			buddyscreen.statusBanner.setIcon(BuddyListField.onlineIcon);
//			buddyscreen.statusBanner.setText("Available");
//			buddyscreen.statusBanner.setDirty(true);
			break;
		
		case BTalk.STATE_WAITING:
			buddyscreen.statusBanner = new IconLabelField(BuddyListField.offlineIcon, "Will reconnect in "+String.valueOf(SavedData.retryDelay)+"s...");
			buddyscreen.setTitle(buddyscreen.statusBanner);
//			buddyscreen.statusBanner.setIcon(BuddyListField.offlineIcon);
//			buddyscreen.statusBanner.setText("Waiting to reconnect...");
//			buddyscreen.statusBanner.setDirty(true);
			break;
			
		case BTalk.STATE_RETRYING:
			buddyscreen.statusBanner = new IconLabelField(BuddyListField.offlineIcon, "Reconnecting...");
			buddyscreen.setTitle(buddyscreen.statusBanner);
//			buddyscreen.statusBanner.setIcon(BuddyListField.offlineIcon);
//			buddyscreen.statusBanner.setText("Reconnecting...");
//			buddyscreen.statusBanner.setDirty(true);
			break;
			
		case BTalk.STATE_FAILED:
			buddyscreen.statusBanner = new IconLabelField(BuddyListField.offlineIcon, "Offline");
			buddyscreen.setTitle(buddyscreen.statusBanner);
//			buddyscreen.statusBanner.setIcon(BuddyListField.offlineIcon);
//			buddyscreen.statusBanner.setText("Offline");
//			buddyscreen.statusBanner.setDirty(true);
			break;
			
		default:
			System.out.println("Unhandled state value" + String.valueOf(s));
			break;
		}
		
		if (customText)
			buddyscreen.statusBanner.setText(text);
		
	}
	
	public void logoffJxa() {
		jxa.logoff();
	}
	
	public void exitBtalk() {
		Notification.clearNotification();
		System.exit(0);
	}
	
//	public void notifyNewMessage(String from, boolean isCurrentBuddy) {
//		if (!this.isForeground()) {
//			this.requestForeground();
//		}
//		Alert.startVibrate(300);
//		LED.setConfiguration(500, 250, LED.BRIGHTNESS_50);
//		LED.setState(LED.LED_TYPE_STATUS, LED.STATE_BLINKING);		
//	}
	
	private void authBtalkHandler() {
		if (this.state == BTalk.STATE_LOGINING) {
			if (loginscreen.saveField.getChecked()) {
				SavedData.setUserInfo(loginscreen.usernameField.getText(), loginscreen.passwordField.getText());
			} else {
				SavedData.destroyUserInfo();
			}
			
			buddyList = new BuddyListField(this);
			buddyscreen = new BuddyScreen(buddyList);
			
			this.popScreen(loginscreen);
			this.pushScreen(buddyscreen);
			loginscreen = null;
		} else if (this.state == BTalk.STATE_RETRYING) {
			System.out.println("Retry successful");
			this.setMyStatus(BTalk.STATE_ONLINE, false, null);
		}
		this.state = BTalk.STATE_ONLINE;
		try {
			jxa.getRoster();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/*
	 * Xmpp event handlers
	 * @see net.sourceforge.jxa.XmppListener#onAuth(java.lang.String)
	 */
	public void onAuth(String resource) {
		System.out.println("Auth: "+resource);
		// TODO Auto-generated method stub
		this.invokeAndWait(new Runnable() {
			public void run() {
				authBtalkHandler();
			}
		});
//		this.state = BTalk.STATE_LOGIN;
//		if (loginscreen.saveField.getChecked()) {
//			SavedData.setUserInfo(loginscreen.usernameField.getText(), loginscreen.passwordField.getText());
//		} else {
//			SavedData.destroyUserInfo();
//		}
//		
//		buddyList = new BuddyListField(this);
//		buddyscreen = new BuddyScreen(buddyList);
//		synchronized(BTalk.getEventLock()) {
//			
//			this.popScreen(loginscreen);
//			this.pushScreen(buddyscreen);
//		}
//		loginscreen = null;
//		
//		try {
//			jxa.getRoster();
//		} catch (IOException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
	}
	
	private void authFailedBtalkHandler(final String msg) {
		this.state = BTalk.STATE_FAILED;
		loginscreen.logginState.setText("Status: Authorisation Failed. Please check username/password and retry");
	}

	public void onAuthFailed(final String message) {
		// TODO Auto-generated method stub
		this.invokeAndWait(new Runnable() {
			public void run() {
				authFailedBtalkHandler(message);
			}
		});
		this.invokeAndWait(new NotifyDialog("Login failed. Please check your username and password"));
	}
	
	public void retryBtalk() {
		jxa.removeListener(buddyscreen.btalk);
		jxa = new Jxa(_jid, _password, "mobile", 10, _server, "5223", true);
		jxa.addListener(buddyscreen.btalk);
		jxa.start();
	}
	
	private void connFailedBtalkHandler(final String msg) {
		if (this.state == BTalk.STATE_ONLINE ||
				this.state == BTalk.STATE_RETRYING) {
			buddyList.invalBuddies();
			
			if (SavedData.autoRetry) {
				this.retryCount++;
				if (SavedData.retryLimit > 0 && this.retryCount > SavedData.retryLimit) {
					this.setMyStatus(BTalk.STATE_FAILED, false, null);
					this.state = BTalk.STATE_FAILED;
					Dialog.alert("Retry count exceeded!");
					return;
				}
				
				this.setMyStatus(BTalk.STATE_WAITING, false, null);
				this.state = BTalk.STATE_WAITING;
				TimerTask retryTask = new TimerTask() {
					public void run() {
						state = BTalk.STATE_RETRYING;
						invokeAndWait(new Runnable() {
							public void run() {
								setMyStatus(BTalk.STATE_RETRYING, false, null);
							}
						});
						retryBtalk();
					}
				};
				
				Timer retrytimer = new Timer();
				retrytimer.schedule(retryTask, SavedData.retryDelay * 1000);
				return;
			} else {
				this.state = BTalk.STATE_FAILED;
			}
				
		} else if (this.state == BTalk.STATE_LOGINING) {
			this.state = BTalk.STATE_FAILED;
			loginscreen.logginState.setText("Status: Connection failed, please retry");			
		}
		Dialog.alert("Connection lost. Please reconnect.\nInfo: "+msg);
//		this.invokeAndWait(new NotifyDialog("Connection lost. Please reconnect.\nInfo: "+msg));
	}

	public void onConnFailed(final String msg) {
		System.out.println("con failed: "+msg);
		// TODO Auto-generated method stub
		this.invokeAndWait(new Runnable() {
			public void run() {
				connFailedBtalkHandler(msg);
			}
		});
//		if (this.state == BTalk.STATE_LOGIN) {
//			this.state = BTalk.STATE_FAILED;
//			this.invokeAndWait(new Runnable() {
//				public void run() {
//					buddyList.clearBuddies();
//				}
//			});
//		}
//		this.invokeAndWait(new NotifyDialog("Connection lost. Please reconnect.\nInfo: "+msg));
	}
	
	private void contactBtalkHandler(final String jid, final String name, final String group, final String subscription) {
		if (subscription.equals("both") && buddyList.findBuddyIndex(jid) == -1) {
			buddyList.addBuddy(new Buddy(jid, name, Buddy.STATUS_OFFLINE));
		}
	}

	public void onContactEvent(final String jid, final String name, final String group,
			final String subscription) {
		System.out.println("[debug] contact: "+jid+" "+name+" "+group+ " "+subscription);
		// TODO Auto-generated method stub
		this.invokeLater(new Runnable() {
			public void run() {
				contactBtalkHandler(jid, name, group, subscription);
			}
		});
//		if (subscription.equals("both") && buddyList.findBuddyIndex(jid) == -1) {
//			buddyList.addBuddy(new Buddy(jid, name, Buddy.STATUS_OFFLINE));
//			this.invokeAndWait(new Runnable() {
//				public void run() {
//					buddyList.addBuddy(new Buddy(jid, name, Buddy.STATUS_OFFLINE));
//				}
//			});
//		}
	}

	public void onContactOverEvent() {
		System.out.println("over");
		// TODO Auto-generated method stub
		
	}
	
	private void messageBtalkHandler(final String from, final String body) {
		boolean isCurrentBuddy;
		if (currentBuddy != null && currentBuddy.jid.equals(from)) {
			isCurrentBuddy = true;
//			synchronized (BTalk.getEventLock()) {
//				currentBuddy.receiveMessage(body, true);
//			}

			currentBuddy.receiveMessage(body, true);
		// from other buddy
		} else {
			isCurrentBuddy = false;
			final int idx = buddyList.findBuddyIndex(from);
			if (idx != -1) {
//				synchronized (BTalk.getEventLock()) {
//					Buddy b = buddyList.getBuddyAt(idx);
//					b.receiveMessage(body, false);
//					if (!b.unread) {
//						// be careful of the competition access of unreadCount 
//						synchronized (unreadLock) {
//							++unreadCount;							
//						}
//						b.unread = true;
//						buddyList.buddyReposition(idx);
//					}
//				}
				final Buddy b = buddyList.getBuddyAt(idx);
				if (!b.unread) {
					synchronized (unreadLock) {
						++unreadCount;
					}
					b.unread = true;
				}
				b.receiveMessage(body, false);
				buddyList.buddyReposition(idx);
			} else {
				System.out.println("[warning] Message from unkown buddy");
			}
		}
		Notification.newMessage(from, isCurrentBuddy);		
	}

	public void onMessageEvent(final String from, final String body) {
		System.out.println("[debug] message: "+from+" "+ body);
		// TODO add notify function
		this.invokeLater(new Runnable() {
			public void run() {
				messageBtalkHandler(from, body);	
			}
		});
		
		// 1. from current buddy
		/*boolean isCurrentBuddy;
		if (currentBuddy != null && currentBuddy.jid.equals(from)) {
			isCurrentBuddy = true;
			this.invokeAndWait(new Runnable() {
				public void run() {
					currentBuddy.receiveMessage(body, true);
				}
			});
		// from other buddy
		} else {
			isCurrentBuddy = false;
			final int idx = buddyList.findBuddyIndex(from);
			if (idx != -1) {
				final Buddy b = buddyList.getBuddyAt(idx);
				if (!b.unread) {
					synchronized (unreadLock) {
						++unreadCount;
					}
					b.unread = true;
				}
				this.invokeLater(new Runnable() {
					public void run() {
						b.receiveMessage(body, false);
						buddyList.buddyReposition(idx);
					}
				});
			} else {
				System.out.println("[warning] Message from unkown buddy");
			}
		}
		Notification.newMessage(from, isCurrentBuddy);*/
	}
	
	private void statusBtalkHandler(final String jid, String show, String status) {
		int idx = buddyList.findBuddyIndex(jid);
		
		if (idx != -1) {
			int state = 0;
			if (show.equals(""))
				state = Buddy.STATUS_ONLINE;
			else if (show.equals("away"))
				state = Buddy.STATUS_AWAY;
			else if (show.equals("dnd"))
				state = Buddy.STATUS_BUSY;
			else if (show.equals("na"))
				state = Buddy.STATUS_OFFLINE;
			else
				System.out.println("Unhandled status:" + show);
			
			((Buddy)buddyList.buddyVector.elementAt(idx)).status = state;
			buddyList.buddyReposition(idx);
		} else {
			System.out.println("No buddy matches");
		}
	}

	public void onStatusEvent(final String jid, final String show, final String status) {
		System.out.println("[debug] status:"+jid+" "+show+ " "+ status);
		// TODO Auto-generated method stub
		int idx = jid.indexOf('/');
		final String id;
		// in some instance, jid contains no '/', fix this
		if (idx == -1) {
			id = new String(jid);
		} else {
			id = jid.substring(0, jid.indexOf('/'));
		}
		
		this.invokeLater(new Runnable() {
			public void run() {
				statusBtalkHandler(id, show, status);
			}
		});
//		idx = buddyList.findBuddyIndex(id);
//		
//		if (idx != -1) {
//			int state = 0;
//			if (show.equals(""))
//				state = Buddy.STATUS_ONLINE;
//			else if (show.equals("away"))
//				state = Buddy.STATUS_AWAY;
//			else if (show.equals("dnd"))
//				state = Buddy.STATUS_BUSY;
//			else if (show.equals("na"))
//				state = Buddy.STATUS_OFFLINE;
//			else
//				System.out.println("Unhandled status:" + show);
//			
//			((Buddy)buddyList.buddyVector.elementAt(idx)).status = state;
//			buddyList.buddyReposition(idx);
//		} else {
//			System.out.println("No buddy matches");
//		}
	}

	public void onSubscribeEvent(String jid) {
		// TODO Auto-generated method stub
		
	}

	public void onUnsubscribeEvent(String jid) {
		// TODO Auto-generated method stub
		
	}
}
